package com.qhnu.zxjx.aspect;

import com.alibaba.fastjson.JSON;
import com.qhnu.zxjx.annotation.RequestLog;
import com.qhnu.zxjx.context.AppContext;
import com.qhnu.zxjx.util.TimeUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 日志记录aop
 * @author: panpei
 * @date: 2019/7/25 13:31
 * @version: 1.0
 **/
@Aspect
@Component
public class RequestLogAspect {

	private Logger logger = LogManager.getLogger(getClass());

	@Pointcut("@annotation(com.qhnu.zxjx.annotation.RequestLog)")
	private void pointcut() {
	}

	@Around("pointcut()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

		// 目标类名称
		String clazzName = "";

		// 方法名
		String methodName = "";

		// ip
		String ip = "";

		// 请求url
		String requestUrl = "";

		// 请求方法
		String requestType = "";

		RequestLog requestLog = null;

		try {
			clazzName = joinPoint.getTarget().getClass().getName();

			methodName = joinPoint.getSignature().getName();

			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
			// ip
			ip = getIpAddress(request);

			// 请求url
			requestUrl = request.getRequestURL().toString();

			// 请求方法
			requestType = request.getMethod();

			// 参数
			requestLog = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(RequestLog.class);
			Map<String, Object> paramMap = new HashMap<>();
			if (requestLog != null && requestLog.parameter()) {
				Object[] args = joinPoint.getArgs();
				String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
				if (args != null && argNames != null && args.length <= argNames.length) {
					for (int i = 0; i < args.length; i++) {
						if (args[i] instanceof MultipartFile || args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse) {
							continue;
						}
						if (requestLog.upload()) {
							if (args[i] == null || isJavaClass(args[i].getClass())) {
								paramMap.put(argNames[i], args[i]);
							} else {
								paramMap.put(argNames[i], getKeyAndValue(args[i]));
							}
						} else {
							paramMap.put(argNames[i], args[i]);
						}
					}
				}
			}

			// 参数
			String params = JSON.toJSONString(paramMap);

			logger.info("请求时间:{}, 请求ip：{}, 请求url:{}, 请求类型：{}, 请求类名：{}, 请求方法名：{}, 参数：{}", TimeUtil.date2String(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"), ip, requestUrl, requestType, clazzName, methodName, params);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("记录请求参数失败, e:{}", e.getMessage());
		}

		Long startTime = System.currentTimeMillis();

		// 调用目标方法
		Object result = joinPoint.proceed();

		Long endTime = System.currentTimeMillis();

		try {
			logger.info("请求时间:{}, 请求ip：{}, 请求url:{}, 请求类型：{}, 请求类名：{}, 请求方法名：{}, 返回值:{}, 耗时：{}ms", TimeUtil.date2String(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"), ip, requestUrl, requestType, clazzName, methodName, requestLog == null || !requestLog.response() ? null : requestLog.download() ? "{\"download\":\"true\"}" : JSON.toJSONString(result), endTime - startTime);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("记录请求返回值和耗时失败, e:{}", e.getMessage());
		}
		return result;
	}

	private static String getIpAddress(HttpServletRequest request) {
		// 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址
		String ip = request.getHeader("X-Forwarded-For");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("Proxy-Client-IP");
			}
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("WL-Proxy-Client-IP");
			}
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("HTTP_CLIENT_IP");
			}
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("HTTP_X_FORWARDED_FOR");
			}
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getRemoteAddr();
			}
		} else if (ip.length() > 15) {
			String[] ips = ip.split(",");
			for (int index = 0; index < ips.length; index++) {
				String strIp = (String) ips[index];
				if (!("unknown".equalsIgnoreCase(strIp))) {
					ip = strIp;
					break;
				}
			}
		}
		return ip;
	}

	private static Map<String, Object> getKeyAndValue(Object obj) {
		Map<String, Object> map = new HashMap<>();
		// 得到类对象
		Class userCla = (Class) obj.getClass();
		// 得到类中的所有属性集合
		Field[] fs = userCla.getDeclaredFields();
		for (int i = 0; i < fs.length; i++) {
			Field f = fs[i];
			if (f.getType().equals(MultipartFile.class)) {
				continue;
			}
			// 设置些属性是可以访问的
			f.setAccessible(true);
			Object val = new Object();
			try {
				val = f.get(obj);
				// 得到此属性的值
				// 设置键值
				map.put(f.getName(), val);
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		return map;
	}

	private static boolean isJavaClass(Class<?> clz) {
		return clz != null && clz.getClassLoader() == null;
	}
}
